package main

import (
	"fmt"
	"math/rand"
	"sort"
)

func main() {
	fmt.Println("~~~~~~~~~~第一大题~~~~~~~~~~~~")
	// 请问s1、s2内各有什么元素？
	// s1修改一个元素会影响s2吗？
	// s2修改一个元素会影响s1吗？ s2再增加一个元素会怎么样？

	s1 := make([]int, 3, 4)
	s2 := append(s1, 1)

	fmt.Println("~~~~~~~~~~~s1~~~~~~~~~~~")
	fmt.Printf("type: %T\nvalue: %[1]v\n", s1)
	fmt.Println("~~~~~~~~~~~s2~~~~~~~~~~~")
	fmt.Printf("type: %T\nvalue: %[1]v\n", s2)
	fmt.Println("问题一：s1有3个元素，s2有4个元素")
	fmt.Println("~~~~~~~~~~~~~~~~~~~~~~")

	s1[2] = 2
	fmt.Println(s1)
	fmt.Println(s2)
	fmt.Println("~~~~~~~~~~~~~~~~~~~~~~")
	s2[1] = 1
	fmt.Println(s2)
	fmt.Println(s1)
	fmt.Println("问题二：s1修改一个元素影响s2。s2修改一个元素也影响s1。因为他们共享底层数组")
	fmt.Printf("s1: header_addr=%p array_addr=%p len=%d cap=%d\n", &s1, &s1[0], len(s1), cap(s1))
	fmt.Printf("s2: header_addr=%p array_addr=%p len=%d cap=%d\n", &s2, &s2[0], len(s2), cap(s2))
	fmt.Println("~~~~~~~~~~~~~~~~~~~~~~")

	s2 = append(s2, 22)
	fmt.Println(s2)
	fmt.Println(s1)
	fmt.Printf("s1: header_addr=%p array_addr=%p len=%d cap=%d\n", &s1, &s1[0], len(s1), cap(s1))
	fmt.Printf("s2: header_addr=%p array_addr=%p len=%d cap=%d\n", &s2, &s2[0], len(s2), cap(s2))
	fmt.Println("问题三：s2再增加一个元素只对自己可见，而s1不可见。因为通过s1的声明可知len(s1)=3。当s2再增加一个元素，元素的个数将超过底层数组的cap，所以发生了扩容，也就意味着s2切片不再与s1共享底层数组，s2的底层数组len=5 cap=8")

	fmt.Println("~~~~~~~~~~~第二大题~~~~~~~~~~~")
	// 有一个数组 [1,4,9,16,2,5,10,15]，生成一个新切片，要求新切片元素是数组相邻2项的和。
	a := [8]int{1, 4, 9, 16, 2, 5, 10, 15}
	fmt.Printf("%T\n", a)
	s3 := []int{}
	for _, v := range a {
		s3 = append(s3, v)
	}
	fmt.Println(s3)
	fmt.Printf("%T\n", s3)
	s4 := []int{}
	for i := range a {
		if i == 0 {
			s4 = append(s4, s3[i+1])
		} else if i > 0 && i < (len(s3)-1) {
			s4 = append(s4, s3[i-1]+s3[i+1])
		} else {
			s4 = append(s4, s3[i-1])
		}
	}
	fmt.Println("新切片：", s4)

	fmt.Println("~~~~~~~~~~第三大题~~~~~~~~~~~~")
	// 数字重复统计
	// - 随机产生100个整数
	// - 数字的范围[-100, 100]
	// - 降序输出这些生成的数字并打印其重复的次数
	sliceA := []int{}
	for i := 0; i < 100; i++ {
		// 用0、1分别代表负数、正数
		ifMinus := rand.Intn(2)
		if ifMinus == 1 {
			sliceA = append(sliceA, rand.Intn(101))
		} else {
			sliceA = append(sliceA, -rand.Intn(101))
		}
	}
	fmt.Println("原始: ", sliceA)
	sort.Sort(sort.Reverse(sort.IntSlice(sliceA)))
	fmt.Println("降序: ", sliceA)

	mapDedup := map[int]int{}
	for _, v := range sliceA {
		mapDedup[v] = 0
	}
	fmt.Println("map key包含: ")
	for k := range mapDedup {
		fmt.Printf("%d,", k)
	}
	fmt.Printf("有%d个数字存在重复", 100-len(mapDedup))

	fmt.Println("~~~~~~~~~~~第四大题~~~~~~~~~~~")
	// Q:简述线性数据结构和哈希表优劣

	// A：
	// 1 数据结构
	// 1.1 从数据结构看，线性表分为顺序表和链接表，其中
	// （1）顺序表：在内存中使用连续的内存，扩容需要新开辟内存空间
	// （2）链接表：在内存中将分散的元素链接起来，不需要连续的内存，一般不涉及扩容问题
	// 1.2 哈希表的数据结构特点，对比线性表
	// （1）扩容问题：需要根据元素数量采取相应的扩容策略（增量/等量扩容）
	// 2 CURD效率:
	// （1）哈希表增删改都不涉及数据的挪动，而顺序表则需要区分首部、中间、尾部的效率；所以整体来看，【增删改场景哈希表开销较小】。
	// （2）在查询方面，由于哈希表有key查找，而顺序表有索引查找，所以都可以做到O(1)。当然，如果都通过内容遍历查找，平均效率都不高；【查询方面平均效率最低的是链接表】，因为不支持索引，只能通过首地址+索引*单元的字节数查找中间元素
	// 3 适用场景：
	// （1）消息队列：使用线性表-Queue更适合，因为线性表元素之间存在Sequence关系，而【哈希表不太适用于这种需要解决前后依赖关系场景】
	// （2）需要大量增删改的缓存场景：对于这类缓存场景，使用哈希表的KV特性来存储数据更合适，因为除了可以通过Key的O(1)复杂度查找元素，相对线性表的优势也体现在元素的增删改场景无需数据挪动
	// 4 排序：线性表元素排序比较方便，而哈希表需要先将元素遍历实现线性化，然后再进行排序
}
